/*
 * Copyright (C) 2025 Thomas Akehurst
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.github.tomakehurst.wiremock.db.service.impl;

import com.github.tomakehurst.wiremock.common.Json;
import com.github.tomakehurst.wiremock.db.entity.MultiMappingEntity;
import com.github.tomakehurst.wiremock.db.entity.StubMappingEntity;
import com.github.tomakehurst.wiremock.db.mapper.MultiMappingMapper;
import com.github.tomakehurst.wiremock.db.mapper.StubMappingMapper;
import com.github.tomakehurst.wiremock.db.service.MultiMappingService;
import com.github.tomakehurst.wiremock.db.service.StubMappingService;
import com.github.tomakehurst.wiremock.db.utils.StubMappingConverter;
import com.github.tomakehurst.wiremock.matching.RequestPattern;
import com.github.tomakehurst.wiremock.stubbing.StubMapping;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.flywaydb.core.internal.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * 单存根业务实现类
 * 实现 StubMappingService 接口，封装单存根CRUD、多存根关联逻辑，确保事务一致性
 */
public class StubMappingServiceImpl implements StubMappingService {

    private static final Logger LOG = LoggerFactory.getLogger(StubMappingServiceImpl.class);
    private static final int DEFAULT_PAGE_NUM = 1;
    private static final int DEFAULT_PAGE_SIZE = 10;
    private static final int MAX_PAGE_SIZE = 100; // 限制最大分页条数，避免性能问题

    // 依赖注入：MyBatis会话工厂 + 转换器 + 多存根服务（解耦依赖）
    private final SqlSessionFactory sqlSessionFactory;
    private final StubMappingConverter stubMappingConverter;
    private final MultiMappingService multiMappingService;

    /**
     * 构造器注入所有依赖（推荐通过Spring自动注入，避免硬编码）
     * @param sqlSessionFactory MyBatis会话工厂
     * @param stubMappingConverter 单存根实体转换器
     * @param multiMappingService 多存根业务服务
     */
    public StubMappingServiceImpl(
            SqlSessionFactory sqlSessionFactory,
            StubMappingConverter stubMappingConverter,
            MultiMappingService multiMappingService
    ) {
        this.sqlSessionFactory = sqlSessionFactory;
        this.stubMappingConverter = stubMappingConverter;
        this.multiMappingService = multiMappingService;
    }

    @Override
    public void save(StubMapping stubMapping) {
        // 1. 基础参数校验
        if (stubMapping == null) {
            throw new IllegalArgumentException("待保存的单Stub不能为空");
        }
        // 自动生成StubID（确保唯一性）
        UUID stubId = stubMapping.getId() == null ? UUID.randomUUID() : stubMapping.getId();
        String stubIdStr = stubId.toString();

        // 业务规则校验：匿名用户（无createUserId）不能创建无权限Stub
        if (!stubMapping.isPermission() && stubMapping.getCreateUserId() == null) {
            LOG.warn("匿名用户尝试创建无权限Stub：stubId={}", stubIdStr);
            throw new IllegalArgumentException("匿名用户无法创建permission=false的Stub：stubId=" + stubIdStr);
        }

        // 2. 开启手动事务（确保「多存根关联 + 单存根保存」原子性）
        try (SqlSession session = sqlSessionFactory.openSession(false)) {
            StubMappingMapper stubMapper = session.getMapper(StubMappingMapper.class);
            MultiMappingMapper multiMapper = session.getMapper(MultiMappingMapper.class);

            try {
                // ------------------------------
                // 步骤1：处理多存根关联（自动创建/更新扩展表）
                // ------------------------------
                String contentId;
                MultiMappingEntity existingMulti = null;
                try {
                    // 检查当前Stub是否已关联多存根文件
                    existingMulti = multiMappingService.findByStubId(stubIdStr);
                } catch (RuntimeException e) {
                    // 未关联多存根文件，直接进入新建逻辑
                    LOG.debug("单Stub未关联多存根文件，将新建关联：stubId={}", stubIdStr);
                }

                // ------------------------------
                // 步骤2：拼接Stub的URL（需求ID + 原始URL）
                // ------------------------------
                RequestPattern originalRequest = stubMapping.getRequest();
                String originalUrl = originalRequest != null ? originalRequest.getUrl() : "";
                String demandId = stubMapping.getDemandId();
                String updatedUrl = buildUpdatedUrl(demandId, originalUrl);
                // 更新URL到StubMapping
                if (originalRequest != null) {
                    stubMapping.setRequest(originalRequest.withUpdatedUrl(updatedUrl));
                }

                // ------------------------------
                // 步骤3：处理多存根记录（新建/更新）
                // ------------------------------
                if (existingMulti != null) {
                    // 情况1：已关联 → 更新多存根内容
                    existingMulti.setContent(Json.writePrivate(stubMapping));
                    multiMapper.update(existingMulti);
                    contentId = existingMulti.getId();
                    LOG.debug("更新多存根关联记录：stubId={}, contentId={}", stubIdStr, contentId);
                } else {
                    // 情况2：未关联 → 新建多存根记录
                    MultiMappingEntity newMulti = new MultiMappingEntity();
                    newMulti.setId(UUID.randomUUID().toString()); // 生成contentId
                    newMulti.setContent(Json.writePrivate(stubMapping)); // 存储Stub完整JSON
                    newMulti.setStubId(stubIdStr); // 关联单StubID
                    multiMapper.insert(newMulti);
                    contentId = newMulti.getId();
                    LOG.debug("新建多存根关联记录：stubId={}, contentId={}", stubIdStr, contentId);
                }

                // ------------------------------
                // 步骤4：保存单存根记录（关联contentId）
                // ------------------------------
                StubMappingEntity stubEntity = stubMappingConverter.toEntity(stubMapping, contentId);
                stubEntity.setId(stubIdStr);
                stubEntity.setPermission(stubMapping.isPermission());
                stubEntity.setUpdatedAt(LocalDateTime.now());
                stubEntity.setOriginalFilename(stubMapping.getName());
                if (stubMapper.findById(stubIdStr) != null) {
                    // 已存在 → 更新
                    stubMapper.update(stubEntity);
                    LOG.info("更新单Stub成功：stubId={}, contentId={}", stubIdStr, contentId);
                } else {
                    // 不存在 → 新增
                    stubEntity.setCreatedAt(LocalDateTime.now());
                    stubMapper.insert(stubEntity);
                    LOG.info("新增单Stub成功：stubId={}, contentId={}", stubIdStr, contentId);
                }

                // 提交事务（所有操作成功才生效）
                session.commit();

            } catch (Exception e) {
                // 异常回滚，避免数据不一致
                session.rollback();
                LOG.error("保存单Stub失败（已回滚）：stubId={}", stubIdStr, e);
                throw new RuntimeException("保存单Stub失败（已回滚）：stubId=" + stubIdStr, e);
            }
        }
    }

    @Override
    public void deleteById(UUID id) {
        if (id == null) {
            throw new IllegalArgumentException("单StubID不能为空");
        }
        String stubIdStr = id.toString();

        try (SqlSession session = sqlSessionFactory.openSession(true)) {
            StubMappingMapper mapper = session.getMapper(StubMappingMapper.class);
            // 校验单Stub是否存在
            StubMappingEntity entity = mapper.findById(stubIdStr);
            if (entity == null) {
                LOG.warn("删除失败：单Stub不存在：stubId={}", stubIdStr);
                throw new RuntimeException("单Stub不存在：stubId=" + stubIdStr);
            }

            // 删除单Stub（多存根记录可保留或联动删除，根据业务需求调整）
            mapper.deleteById(stubIdStr);
            LOG.info("删除单Stub成功：stubId={}", stubIdStr);
        } catch (Exception e) {
            LOG.error("删除单Stub失败：stubId={}", stubIdStr, e);
            throw new RuntimeException("删除单Stub失败：stubId=" + stubIdStr, e);
        }
    }

    @Override
    public void deleteAll() {
        try (SqlSession session = sqlSessionFactory.openSession(true)) {
            StubMappingMapper mapper = session.getMapper(StubMappingMapper.class);
            int deleteCount = mapper.deleteAll();
            LOG.info("删除所有单Stub成功：共删除 {} 条记录", deleteCount);
        } catch (Exception e) {
            LOG.error("删除所有单Stub失败", e);
            throw new RuntimeException("删除所有单Stub失败：" + e.getMessage(), e);
        }
    }

    @Override
    public List<StubMapping> findAll() {
        try (SqlSession session = sqlSessionFactory.openSession()) {
            StubMappingMapper mapper = session.getMapper(StubMappingMapper.class);
            List<StubMappingEntity> entities = mapper.findAll();
            LOG.info("查询到 {} 条单Stub记录", entities.size());

            // 委托转换器批量转换（Entity → StubMapping，含多存根内容解析）
            return stubMappingConverter.batchToStubMapping(entities);
        } catch (Exception e) {
            LOG.error("查询所有单Stub失败", e);
            throw new RuntimeException("查询所有单Stub失败：" + e.getMessage(), e);
        }
    }

    @Override
    public List<StubMapping> findByContentId(String contentId) {
        if (contentId == null || contentId.trim().isEmpty()) {
            throw new IllegalArgumentException("多存根文件ID（contentId）不能为空");
        }

        try (SqlSession session = sqlSessionFactory.openSession()) {
            StubMappingMapper mapper = session.getMapper(StubMappingMapper.class);
            List<StubMappingEntity> entities = mapper.findByContentId(contentId);
            LOG.info("查询到 contentId={} 关联的单Stub记录 {} 条", contentId, entities.size());

            return stubMappingConverter.batchToStubMapping(entities);
        } catch (Exception e) {
            LOG.error("根据contentId查询单Stub失败：contentId={}", contentId, e);
            throw new RuntimeException("根据contentId查询单Stub失败：contentId=" + contentId, e);
        }
    }


    /**
     * 条件分页查询单存根（支持需求ID、权限状态、创建人ID筛选）
     * @param demandId 需求ID（可选，null则不筛选）
     * @param createUserId 创建人ID（可选，null则不筛选）
     * @param pageNum 页码（默认1）
     * @param pageSize 每页条数（默认10）
     * @return 符合条件的单存根列表
     */
    @Override
    public List<StubMapping> findByConditions(
            String name,
            String demandId,
            String createUserId,
            LocalDateTime updatedAt,
            Integer pageNum,
            Integer pageSize){
        // 1. 分页参数处理（计算 offset，适配 Mapper 的传统分页）
        int validPageNum = (pageNum == null || pageNum < 1) ? 1 : pageNum;
        int validPageSize = (pageSize == null || pageSize < 9) ? 10 : pageSize;
        validPageSize = Math.min(validPageSize, 100); // 限制最大页大小，防内存溢出
        int offset = (validPageNum - 1) * validPageSize; // 计算偏移量（传统分页核心）

        // 2. 查询条件过滤（空值/无效值自动排除，不参与筛选）
        // 2.1 模糊查询参数：空值/长度<2 → 设为null（减少全表扫描）
        name = (StringUtils.hasText(name) && name.length() >= 2) ? name : null;
        demandId = (StringUtils.hasText(demandId) && demandId.length() >= 2) ? demandId : null;
        // 2.2 精确查询参数：空值 → 设为null
        createUserId = StringUtils.hasText(createUserId) ? createUserId : null;
        // 2.3 时间参数：空值 → 设为null（不筛选时间）

        // 3. 无条件查询防护（避免全表扫描，可选补充默认条件）
        boolean hasAnyCondition = (name != null || demandId != null || createUserId != null || updatedAt != null);
        if (!hasAnyCondition) {
            LOG.warn("未输入任何查询条件，将按默认规则分页查询（已限制页大小，避免全表返回）");
            // 可选：无条件时默认查询“近30天”数据，进一步减少扫描范围
            if (updatedAt == null) {
                updatedAt = LocalDateTime.now().minusDays(30);
                LOG.info("自动补充时间条件：查询近30天更新的数据（updatedAt >= {}）", updatedAt);
            }
        }

        // 4. 调用 Mapper 执行查询（try-with-resources 自动关闭 SqlSession）
        try (SqlSession session = sqlSessionFactory.openSession(true)) { //  查询无需事务，自动提交
            StubMappingMapper mapper = session.getMapper(StubMappingMapper.class);

            // 打印查询日志（便于排查问题）
            LOG.debug("Stub条件查询：name={}, demandId={}, createUserId={}, updatedAt>={}, pageNum={}, pageSize={}, offset={}",
                    name, demandId, createUserId, updatedAt, validPageNum, validPageSize, offset);

            // 调用 Mapper 接口（空条件会被 XML 自动忽略）
            List<StubMappingEntity> entityList = mapper.findByConditionsWithPage(
                    name,          // 可能为null → XML 不拼接该条件
                    demandId,      // 可能为null → XML 不拼接该条件
                    createUserId,  // 可能为null → XML 不拼接该条件
                    updatedAt,     // 可能为null → XML 不拼接该条件
                    offset,        // 分页偏移量（必不为null）
                    validPageSize  // 页大小（必不为null）
            );

            // 5. 实体转换（数据库实体 → 业务模型）
            if (entityList.isEmpty()) {
                LOG.info("未查询到符合条件的 Stub 数据（页码：{}，页大小：{}）", validPageNum, validPageSize);
                return Collections.emptyList();
            }
            LOG.info("查询到符合条件的 Stub 数据共 {} 条（页码：{}，页大小：{}）",
                    entityList.size(), validPageNum, validPageSize);

            return entityList.stream()
                    .map(stubMappingConverter::toStubMapping) // 自定义转换器，隔离数据库实体
                    .collect(Collectors.toList());

        } catch (Exception e) {
            // 6. 异常包装（转为业务异常，便于上层统一处理）
            String errorMsg = String.format(
                    "Stub 条件查询失败（姓名=%s, 需求编号=%s, 创建人=%s, 修改时间=%s）",
                    name, demandId, createUserId, updatedAt
            );
            LOG.error(errorMsg, e);
            throw new RuntimeException(errorMsg, e); // 可替换为自定义业务异常（如 StubQueryException）
        }
}

    /**
     * 私有工具方法：拼接需求ID与原始URL
     * @param demandId 需求ID（可为null）
     * @param originalUrl 原始URL（可为null）
     * @return 拼接后的URL
     */
    private String buildUpdatedUrl(String demandId, String originalUrl) {
        if (demandId == null || demandId.trim().isEmpty()) {
            return originalUrl != null ? originalUrl : "";
        }
        if (originalUrl == null || originalUrl.trim().isEmpty()) {
            return "/" + demandId;
        }
        // 确保URL格式统一（避免重复斜杠）
        return originalUrl.startsWith("/") ? "/" + demandId + originalUrl : "/" + demandId + "/" + originalUrl;
    }
}
    